#include "nr_net_server.h"
#include <QTcpServer>

nr_net_server::nr_net_server(QObject *parent, nr_object_manager& object_manager)
    : QObject(parent)
    , m_tcp_server(NULL)
    , m_port(NR_NET_DEFAULT_PORT)
    , m_logic(object_manager)
{
}

nr_net_server::~nr_net_server(void)
{
    if (m_tcp_server)
    {
        delete m_tcp_server;
    }
}

nr_error_t nr_net_server::start()
{
    if (m_tcp_server != NULL)
        return NR_ERROR_NONE;

    if (m_port == -1)
        return NR_ERROR_INVALID_PORT;

    m_tcp_server = new QTcpServer(this);
    bool ret = m_tcp_server->listen(QHostAddress::Any, m_port);
    Q_ASSERT(ret);
    if (!ret)
    {
        qDebug("NR-Server listening on port %d failed!", m_port);
        return NR_ERROR_NETWORK_PORT_USED;
    }

    ret = connect(m_tcp_server, SIGNAL(newConnection()), this, SLOT(accept_connection()));
    Q_ASSERT(ret);
    if (!ret)
    {
        delete m_tcp_server;
    }

    qDebug("NR-Server listening on port %d succeeded!", m_port);
    return NR_ERROR_NONE;
}

nr_error_t nr_net_server::stop()
{
    if (m_tcp_server != NULL)
    {
        m_tcp_server->close();
        delete m_tcp_server;
        m_tcp_server = NULL;
    }

    return NR_ERROR_NONE;
}

void nr_net_server::accept_connection()
{
    qDebug() << "Accept connection";

    QTcpSocket *tcp_socket = m_tcp_server->nextPendingConnection(); 
    nr_net_socket *socket = new nr_net_socket(tcp_socket);
    Q_ASSERT(socket);

    m_net_sockets.insert(socket->session_id(), socket);
    
    bool ret = connect(socket, SIGNAL(net_ready_read(nr_net_socket *)), this, SLOT(read_data(nr_net_socket *))); 
    Q_ASSERT(ret);
}

bool nr_net_server::check_header(const nr_net_command_header& header)
{
    return true;
}

void nr_net_server::read_data(nr_net_socket *net_socket)
{
    Q_ASSERT(net_socket);
    
    nr_net_command_header request_header, reply_header;
    QByteArray request_byte_array, reply_byte_array;
    bool is_close_seesion = false;

    // Read the header 
    qint64 read_len = net_socket->socket()->read((char*)&request_header, sizeof(request_header));
    if (read_len != sizeof(request_header))
    {
        reply_header.status_code = -1;
        reply_header.cmd_type = NR_NET_COMMAND_ERROR;
        goto End;
    }

    if (!check_header(request_header))
    {
        reply_header.status_code = -1;
        reply_header.cmd_type = NR_NET_COMMAND_ERROR;
        goto End;
    }

    // Read the content
    request_byte_array.resize(request_header.content_length);

    // We may wait for some seconds to wait for the content, 
    // because the content may come later than the header
    read_len = net_socket->socket()->read(request_byte_array.data(), request_header.content_length);
    if (read_len != request_header.content_length)
    {
        reply_header.status_code = -1;
        reply_header.cmd_type = NR_NET_COMMAND_ERROR;
        goto End;
    }

    // Parse the command and generate the reply command
    is_close_seesion = m_logic.execute(*net_socket, request_header, request_byte_array, reply_header, reply_byte_array);
    reply_header.seq_no = request_header.seq_no;
    reply_header.session_id = net_socket->session_id();
    reply_header.content_length = reply_byte_array.size();

End:
    /* Send out the reply */
    qint64 len = net_socket->socket()->write((char*)&reply_header, sizeof(reply_header));
    if (len != sizeof(reply_header))
    {
        // Error occurred, close the session directly
        close_session(net_socket);
        return ;
    }

    len = net_socket->socket()->write(reply_byte_array.data(), reply_byte_array.size());
    if (len != reply_byte_array.size())
    {
        // Error occurred, close the session directly
        close_session(net_socket);
        return ;
    }

    // Need to close the session, maybe met a logout command
    if (is_close_seesion)
    {
        // Error occurred, close the session directly
        close_session(net_socket);
    }

}

void nr_net_server::close_session(nr_net_socket *socket)
{
    Q_ASSERT(socket);
    
    // Close the session of the logic module
    m_logic.close_session(socket->session_id());
    nr_net_socket* removed_sock = m_net_sockets.take(socket->session_id());
    Q_ASSERT(removed_sock == socket);

    delete socket;
}


void nr_net_server::timer_check_state()
{

}